/**
*tagfolder
*Licensed under LGPL
*http://code.google.com/p/tagfolder
*chuanshenglu@gmail.com
*xrjieyee@gmail.com
*
*/
#include <TFNamedPipe.h>
#include "TFstd.h"
//Constructors
TFNamedPipe::TFNamedPipe()
#ifdef __WINDOWS_PLATFORM__
	:hPipe(INVALID_HANDLE_VALUE),
	 hFile(INVALID_HANDLE_VALUE),
	 DEFAULT_PIPE_NAME(TEXT("\\\\.\\Pipe\\027C4527-8ED6-4139-A483-14100D002EBD")),
#else
	 :DEFAULT_PIPE_NAME("\\\\.\\Pipe\\027C4527-8ED6-4139-A483-14100D002EBD"),
#endif
	 ServerEnd(false)
{
	ConnectionString.assign(DEFAULT_PIPE_NAME);
}

TFNamedPipe::TFNamedPipe(TFString& conn)
#ifdef __WINDOWS_PLATFORM__
	:hPipe(INVALID_HANDLE_VALUE),
	 hFile(INVALID_HANDLE_VALUE),
	 DEFAULT_PIPE_NAME(TEXT("\\\\.\\Pipe\\027C4527-8ED6-4139-A483-14100D002EBD")),
#else
	 :DEFAULT_PIPE_NAME("\\\\.\\Pipe\\027C4527-8ED6-4139-A483-14100D002EBD"),
	 ServerEnd(false)
#endif
{
	if(conn.empty()==true)
	{
		ConnectionString.assign(DEFAULT_PIPE_NAME);
	}else
	{
		ConnectionString.assign(conn);
	}
}

TFNamedPipe::TFNamedPipe(TFNamedPipe&)
#ifdef __WINDOWS_PLATFORM__
	:hPipe(INVALID_HANDLE_VALUE),
	 hFile(INVALID_HANDLE_VALUE),
	 DEFAULT_PIPE_NAME(TEXT("\\\\.\\Pipe\\027C4527-8ED6-4139-A483-14100D002EBD")),
	 ServerEnd(false)
#endif
{
}
//Destructor
TFNamedPipe::~TFNamedPipe()
{
#ifdef __WINDOWS_PLATFORM__
	if(hPipe==hFile)
	{
		//Server end, hFile==hPipe
		if(hPipe!=INVALID_HANDLE_VALUE)
		{
			CloseHandle(hPipe);
		}
	}else{
		if(hPipe!=INVALID_HANDLE_VALUE)
		{
			CloseHandle(hPipe);
		}
		if(hFile!=INVALID_HANDLE_VALUE)
		{
			CloseHandle(hFile);
		}
	}
	hPipe=INVALID_HANDLE_VALUE;
	hFile=INVALID_HANDLE_VALUE;
#endif
}

///Create a pipe;
bool TFNamedPipe::CreatePipe()
{
	DisconnectFromPipe();
#ifdef __WINDOWS_PLATFORM__
	if(hPipe!=INVALID_HANDLE_VALUE || hFile!=INVALID_HANDLE_VALUE)
	{
		return false;
	}
	hPipe = CreateNamedPipe(
		ConnectionString.c_str(), 
		PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE|PIPE_READMODE_BYTE, 
		1, 
		0, 
		0, 
		1000, 
		NULL);
	if(hPipe== INVALID_HANDLE_VALUE)
	{
		return false;
	}

	hFile=hPipe;
	ServerEnd=true;
#endif
	return true;
}

//Connectio to a existing pipe;
bool TFNamedPipe::ConnectToPipe()
{
#ifdef __WINDOWS_PLATFORM__
	if(ServerEnd)
	{
		SetNamedPipeHandleState(hPipe,PIPE_READMODE_BYTE|PIPE_WAIT,NULL,NULL);
		if(hPipe==INVALID_HANDLE_VALUE || hFile==INVALID_HANDLE_VALUE)
		{
			CreatePipe();
		}
		if(ConnectNamedPipe(hPipe,NULL)==0)
		{
			CloseHandle(hPipe);
			return false;
		}
	}
	else
	{
		if(hFile!=INVALID_HANDLE_VALUE)
		{
			return false;
		}
		if(WaitNamedPipe(ConnectionString.c_str(), NMPWAIT_WAIT_FOREVER)
		   == 0)
		{
			return false;
		}
		hFile = CreateFile(
			ConnectionString.c_str(), 
			GENERIC_READ|GENERIC_WRITE,
			0, 
			(LPSECURITY_ATTRIBUTES)NULL, 
			OPEN_EXISTING,
			FILE_ATTRIBUTE_NORMAL, 
			(HANDLE)NULL
			);
		if(  hFile== INVALID_HANDLE_VALUE )
		{
			return false;
		}
		SetNamedPipeHandleState(hFile,PIPE_READMODE_BYTE|PIPE_WAIT,NULL,NULL);
	}
#endif
	return true;
}
bool TFNamedPipe::DisconnectFromPipe()
{
#ifdef __WINDOWS_PLATFORM__
	if(ServerEnd)
	{
		DisconnectNamedPipe(hPipe);
		//hPipe=INVALID_HANDLE_VALUE;
		//hFile=hPipe;
	}
	else
	{
		CloseHandle(hFile);
		hFile=INVALID_HANDLE_VALUE;
	}
#endif
	return true;
}

char TFNamedPipe::GetChar()
{
	char ret=0;
	Get(&ret,sizeof(char));
	return '#';
}

std::string TFNamedPipe::GetString()
{
	char ch='*';
	std::string ret;

#if defined (__WINDOWS_PLATFORM__)
	ConnectToPipe();
	DWORD ByteRead=1;
	while(ByteRead!=0 && ch!='\0')
	{
		ReadFile(hFile,&ch,sizeof(ch),&ByteRead,NULL);
		ret.push_back(ch);
	}
	DisconnectFromPipe();
#endif

	return ret;
}

bool TFNamedPipe::Put(std::string& str)
{
	return Put ( (void *)(str.c_str()), str.size());
}

//This is two platform related functions of sending and receiving data;
//The put and get primitives are designed to be Synchronous!!!
//Current process/thread can be blocked by these two methods!
bool TFNamedPipe::Put(void *data, unsigned long size)
{
#ifdef __WINDOWS_PLATFORM__
	ConnectToPipe();
	DWORD BytesWritten;
	if(TRUE==WriteFile(hFile, data, size, &BytesWritten, NULL))
	{
		FlushFileBuffers(hFile);
	}
	else
	{
		return false;
	}
	DisconnectFromPipe();
	return true;
#endif
}
unsigned long TFNamedPipe::Get(void *buf, unsigned long size)
{
	if(buf==NULL || size==0){
		return 0;
	}
#ifdef __WINDOWS_PLATFORM__
	ConnectToPipe();
	DWORD BytesRead;
	ReadFile( hFile, buf, size ,&BytesRead, NULL);
	DisconnectFromPipe();
	return (unsigned long)BytesRead;
#else
    return -1;
#endif
}
